home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-02-04 | 62.5 KB | 1,809 lines |
- Path: wugate!wucs1!uunet!lll-winken!ames!ncar!mailrus!ulowell!page
- From: page@swan.ulowell.edu (Bob Page)
- Newsgroups: comp.sources.amiga
- Subject: v89i015: diffdir - print differences between directories
- Message-ID: <11563@swan.ulowell.edu>
- Date: 4 Feb 89 05:00:29 GMT
- Organization: University of Lowell, Computer Science Dept.
- Lines: 1798
- Approved: page@swan.ulowell.edu
-
- Submitted-by: mrr@amanpt1.zone1.com (Mark Rinfret)
- Posting-number: Volume 89, Issue 15
- Archive-name: dos/fs/diffdir.1
-
- # This is a shell archive.
- # Remove everything above and including the cut line.
- # Then run the rest of the file through sh.
- #----cut here-----cut here-----cut here-----cut here----#
- #!/bin/sh
- # shar: Shell Archiver
- # Run the following text with /bin/sh to create:
- # DiffDir.DOC
- # DiffDir.c
- # DiffDir.uu
- # MRDates.c
- # MRDates.h
- # Makefile
- # Sample.Output
- # This archive created: Mon Jan 30 18:38:30 1989
- cat << \SHAR_EOF > DiffDir.DOC
-
- Program:
- DiffDir - List directory differences.
-
- (C)Copyright 1988 by Mark R. Rinfret
- All Rights Reserved.
- This software may be freely distributed for non-profit use only.
- You are free to make changes and redistribute this program as
- long as the source is distributed and this notice is kept intact.
-
- Version:
- 1.0 -01/05/89- Initial release.
-
- Author:
- Mark R. Rinfret
- 348 Indian Ave.
- Portsmouth, RI 02871
- 401-846-7639
-
- Usage:
- DiffDir [>listpath] [-c] [-s scriptfile] dirname1 dirname2
- where
- >listpath redirects standard output to a file or device
- -c ignores filename letter case differences (abc = ABC)
- -s generates a script file for batch file comparisons
-
- Description:
-
- DiffDir compares the contents of two directories, outputting a
- list of differences to standard output. The following conditions
- will generate diagnostic output:
-
- 1. File or directory present in one hierarchy but not the other
- 2. File modification dates differ
- 3. File flags (protection, archive, script, etc.) differ
- 4. File comments differ
- 5. File sizes differ
-
- The -c option will allow DiffDir to ignore filename differences which
- involve different letter case (abc vs. ABC). The -s option will cause
- a line of the form
-
- %COMPARE% path1 path2
-
- to be output to a special script file each time files with similar
- names are found to have different sizes. At this time, it is up to
- the user to edit the script file, substituting the appropriate
- command name for the %COMPARE% meta-string. For instance, "diff -h"
- might be substituted for text file comparisons, "cmp" for binary
- files. A smarter version of this program would more than likely
- do the file content differentiation and apply the correct command
- (defined by default, environment variable or command line parameter)
- to the output string. Any takers?
-
- I wrote DiffDir out of my own need after repeatedly going through the
- drill of listing directories stored on my hard disk and floppy disk
- archives of the same directory to determine which was more current.
- Hopefully this program will save someone else the aggravation.
-
- Mark Rinfret
-
- SHAR_EOF
- cat << \SHAR_EOF > DiffDir.c
- /* DiffDir - Compare directories for differences.
- Filename: DiffDir.c
- (C)Copyright 1988 by Mark R. Rinfret, All Rights Reserved.
- This software may be freely distributed for non-profit use only.
- You are free to make changes and redistribute this program as
- long as the source is distributed and this notice is kept intact.
-
- History (most recent change first):
-
- 12/31/88 V1.0 (MRR)
- Program conception and implementation.
-
- I wrote DiffDir to assist me with configuration management. Though
- I keep all of my PD files on floppy disk, I usually roll them onto
- the hard disk when I want to make changes. Sometimes, I forget to
- copy the hard disk version back to floppy or I forget that I've already
- done it. DiffDir scans two directories and reports the following
- discrepancies:
-
- 1. File dates are different.
- 2. File protection flags are different.
- 3. File names are not exact (case discrepancy).
- 4. File sizes are different.
- 5. File comments are different.
- 6. File exists in one directory but not the other.
-
- DiffDir does not perform file content comparisons. It will, however,
- optionally generate a script for performing comparisons on files whose
- attributes differ.
-
- Usage: DiffDir [-c] [-s scriptfile] [-v] <dir1> <dir2>
- Where:
- -c specifies that letter case should be ignored when comparing
- filenames
-
- -s specifies that a file comparison script is to be output
-
- -v specifies verbose output
-
- <dir1> is the name of the first directory
-
- <dir2> is the name of the second directory
-
- */
-
- #include <stdio.h>
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <libraries/dos.h>
- #include <functions.h>
-
-
- typedef struct fileList {
- USHORT fileCount;
- char *dName; /* directory name for this list */
- struct fileNode *firstEntry, *lastEntry;
- } FileList;
-
- typedef struct fileNode {
- struct fileNode *next, *prev;
- struct fileList *parentList; /* the list that I belong to */
- char *name;
- LONG flags; /* protection, other bits */
- char *comment; /* NULL if comment was empty */
- struct DateStamp date;
- ULONG size; /* in bytes */
- BOOL isDir; /* TRUE => node is a directory */
- struct FileNode *subList; /* sublist for directory node */
- } FileNode;
-
-
- char *DupString();
- FileNode *FindFile();
- void FreeNode();
- char *MakeDirName();
- void *MyAlloc();
- void MyExit();
- void ReportStats();
- void WriteFileInfo();
-
-
- struct FileInfoBlock *fib;
- BOOL ignoreCase = FALSE;
- USHORT level = 0;
- FileList list1, list2;
- LONG maxMemUsed, memInUse;
- BOOL outputScript = FALSE;
- FILE *scriptFile;
- LONG totalFiles, totalDirs;
- BOOL verbose = FALSE;
-
- main(argc, argv)
- int argc; char **argv;
- {
- char flag;
-
- while (--argc > 0 && **++argv == '-') {
- flag = (*argv)[1];
- switch (flag) {
- case 'c':
- ignoreCase = TRUE;
- break;
- case 's':
- if (--argc) {
- ++argv;
- scriptFile = fopen(*argv, "w");
- if (!scriptFile) {
- perror("Script file would not open!");
- exit(1);
- }
- }
- else
- Usage();
- break;
- case 'v':
- verbose = TRUE;
- break;
- default:
- Usage();
- }
- }
- if (argc != 2) Usage();
- list1.dName = MakeDirName("",*argv++);
- list2.dName = MakeDirName("",*argv);
- /* fib must be longword aligned, thus the AllocMem call. */
- fib = AllocMem((long) sizeof(*fib), MEMF_PUBLIC|MEMF_CLEAR);
- if (fib == NULL) {
- printf("DiffDir: unable to allocate file info block!\n");
- goto done;
- }
-
- if (! CollectFiles(&list1))
- if (! CollectFiles(&list2))
- CompareLists(&list1, &list2);
- done:
- if (fib) FreeMem(fib, (long) sizeof(*fib));
- if (verbose) ReportStats();
- }
-
- /* FUNCTION
- AddNode - add file info node to list.
-
- SYNOPSIS
- AddNode(node, list)
- FileNode *node;
- FileList *list;
-
- DESCRIPTION
- AddNode adds the <node> to the <list>. Right now, a very lazy
- approach is taken (adds to end of list). Perhaps later, we'll
- make the list a binary tree or better.
-
- */
-
- void
- AddNode(node, list)
- FileNode *node; FileList *list;
- {
- if (list->firstEntry) { /* List has stuff in it? */
- list->lastEntry->next = node;
- }
- else {
- list->firstEntry = node; /* This is the first entry. */
- }
- node->prev = list->lastEntry;
- list->lastEntry = node;
- ++list->fileCount;
- if (node->isDir)
- ++totalDirs;
- else
- ++totalFiles;
- }
-
- /* FUNCTION
- CollectFiles - collect files for one directory level.
-
- SYNOPSIS
- int CollectFiles(list)
- FileList *list;
-
- DESCRIPTION
- CollectFiles scans the directory pointed to by <list> and creates
- list entry nodes for each file or directory found. A zero is
- returned on success, non-zero otherwise.
- */
-
- int
- CollectFiles(list)
- FileList *list;
- {
- int errCode;
- struct Lock *lock = NULL;
- FileNode *fNode;
- int result = 0;
-
- if (verbose)
- printf("DiffDir: scanning '%s'\n", list->dName);
-
- lock = (struct Lock *) Lock(list->dName, SHARED_LOCK);
- if (lock == NULL) {
- result = IoErr();
- printf("DiffDir: failed to lock '%s'!\n", list->dName);
- goto done;
- }
- if (Examine(lock, fib) == 0) {
- result = IoErr();
- printf("DiffDir: failed to get info for '%s'!\n", list->dName);
- goto done;
- }
-
- if (fib->fib_DirEntryType < 0) {
- result = -1;
- printf("DiffDir: '%s' is not a directory!\n", list->dName);
- goto done;
- }
-
- while (!result && ExNext(lock, fib)) {
- fNode = MyAlloc(sizeof(FileNode));
- fNode->parentList = list;
- fNode->name = DupString(fib->fib_FileName);
- fNode->isDir = (fib->fib_DirEntryType > 0);
- fNode->flags = fib->fib_Protection;
- if (*fib->fib_Comment)
- fNode->comment = DupString(fib->fib_Comment);
- fNode->date = fib->fib_Date;
- fNode->size = fib->fib_Size;
- AddNode(fNode, list);
- }
- errCode = IoErr();
- if (errCode != ERROR_NO_MORE_ENTRIES) {
- result = errCode;
- printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
- }
- done:
- if (lock) UnLock(lock);
- return result;
- }
-
- /* FUNCTION
- CompareLists - compare files and directories in two lists.
-
- SYNOPSIS
- int CompareLists(l1, l2)
- FileList *l1, *l2;
-
- DESCRIPTION
- Comparelists first makes an overall assessment of lists <l1> and
- <l2>. If the number of files/directories in the lists differ,
- that fact is reported. Next, CompareLists tests for the condition
- where an entry in one list has the same name as an entry in the
- other list, but one entry represents a file and the other entry
- represents a directory. These entries are removed from the list.
- CompareFiles is then called to compare all file nodes, removing
- them as they are "used". Finally, CompareDirs is called to
- compare all directory nodes, again removing the nodes as they
- are used. A non-zero return indicates a fatal error.
- */
- int
- CompareLists(l1, l2)
- FileList *l1, *l2;
- {
- static char *isDirMsg = " is a directory";
- static char *isFileMsg = " is a file";
-
- FileNode *f1, *f2, *nextEntry;
- int i;
- int result = 0;
-
- ++level;
- if (verbose) {
- printf("DiffDir: comparing directory\n '%s' to '%s'\n",
- l1->dName, l2->dName);
- }
- /* Scan the lists for nodes whose names match but whose types
- differ (file vs. directory).
- */
- for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
- nextEntry = f1->next;
- f2 = FindFile(f1, l2);
- if (f2 && (f2->isDir != f1->isDir) ) { /* Ooops! */
- printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
- l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
- l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
- FreeNode(f1, l1);
- FreeNode(f2, l2);
- }
- }
- if (! (result = CompareFiles(l1, l2)))
- result = CompareDirs(l1, l2);
- --level;
- return result;
- }
-
- /* FUNCTION
- CompareDirs - compare directory entries.
-
- SYNOPSIS
- int CompareDirs(list1, list2)
- FileList *list1, *list2;
-
- DESCRIPTION
- CompareDirs scans <list1>, attempting to match its directory node
- entries with entries in <list2>. For each matching entry found,
- CompareDirs creates a new sublist, recursively calling CompareLists
- to compare the contents of those directories. When CompareLists
- returns, CompareDirs removes the "used" directory entries from
- both lists. A non-zero return code indicates a fatal error.
- */
-
- int
- CompareDirs(list1, list2)
- FileList *list1, *list2;
- {
- static char *missing = "*** Directory missing: '%s%s'\n";
-
- FileNode *n1, *n2, *nextEntry;
- int result = 0;
- FileList *subList1, *subList2;
-
- for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
- nextEntry = n1->next;
- /* Note: there should only be directory nodes in the list
- at this point!
- */
- if (! n1->isDir) {
- puts("DiffDir: non-directory node found in CompareDirs!");
- MyExit(); /* Dis be real bad! */
- }
- n2 = FindFile(n1, list2);
- if (n2 == NULL) {
- printf(missing, list2->dName, n1->name);
- }
- else {
- subList1 = MyAlloc( sizeof(FileList) );
- subList1->dName = MakeDirName(list1->dName, n1->name);
- subList2 = MyAlloc( sizeof(FileList) );
- subList2->dName = MakeDirName(list2->dName, n2->name);
- result = CollectFiles(subList1);
- if (!result)
- result = CollectFiles(subList2);
- if (!result)
- result = CompareLists(subList1, subList2);
-
- /* Give back the memories :-) */
- free(subList1->dName);
- free(subList1);
- free(subList2->dName);
- free(subList2);
- }
- FreeNode(n1, list1);
- if (n2) FreeNode(n2, list2);
- }
- if (!result) {
- for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
- nextEntry = n2->next;
- printf(missing, list1->dName, n2->name);
- FreeNode(n2, list2);
- }
- }
- return result;
- }
-
- /* FUNCTION
- CompareFile - compare the attributes of two similar files.
-
- SYNOPSIS
- void CompareFile(f1, f2)
- FileNode *f1, *f2;
-
- DESCRIPTION
- CompareFile is called with two file description nodes, <f1> and
- <f2> which are expected to represent similar files in different
- directory hierarchies. CompareFile will report any discrepancies
- to standard output.
- */
- void
- CompareFile(f1, f2)
- FileNode *f1, *f2;
- {
-
- #define NAMES_DONT_MATCH 1
- #define DATES_DONT_MATCH 2
- #define FLAGS_DONT_MATCH 4
- #define SIZES_DONT_MATCH 8
- #define COMMENTS_DONT_MATCH 16
-
- #define ITEM_COUNT 5 /* Make sure this tracks the list above! */
-
- static char *errorDesc[ITEM_COUNT] = {
- " names ", " dates ", " flags ", " sizes ", " comments " };
-
- USHORT error = 0, item, mask;
-
- if (f1->isDir != f2->isDir) {
- puts("*** File type mismatch (file vs. dir) - program error!");
- FreeNode(f1, f1->parentList);
- FreeNode(f2, f2->parentList);
- }
- else {
- if (f1->flags != f2->flags)
- error |= FLAGS_DONT_MATCH;
-
- if (CompareDS(&f1->date, &f2->date))
- error |= DATES_DONT_MATCH;
-
- if (!ignoreCase) {
- if (strcmp(f1->name, f2->name) != 0)
- error |= NAMES_DONT_MATCH;
- }
-
- if (f1->size != f2->size) {
- error |= SIZES_DONT_MATCH;
- if (scriptFile)
- fprintf(scriptFile,"%%COMPARE%% %s%s %s%s\n",
- f1->parentList->dName,f1->name,
- f2->parentList->dName,f2->name);
- }
- if (strcmp(f1->comment, f2->comment) != 0)
- error |= COMMENTS_DONT_MATCH;
- }
- if (error) { /* Aw, darn... */
- printf("*** Mismatch: ");
- for (item = 0, mask = 1; item < ITEM_COUNT;
- ++item, mask= (mask << 1)) {
- if (error & mask)
- printf(errorDesc[item]);
- }
- puts("");
- puts(f1->parentList->dName);
- WriteFileInfo(f1);
- puts("------------------------------------");
- puts(f2->parentList->dName);
- WriteFileInfo(f2);
- puts("====================================");
- }
- }
-
- /* FUNCTION
- CompareFiles - compare all file nodes in two lists.
-
- SYNOPSIS
- int CompareFiles(l1, l2)
- FileList *l1, *l2;
-
- DESCRIPTION
- The file attributes for all files in list <l1> are compared to
- those in list <l2>. Discrepancies are reported to standard
- output. After all the files in <l1> have been tested, a second
- scan is made over list <l2> for any remaining file nodes. These
- represent files which were not found in <l1>. Upon return, all
- file nodes will have been removed from lists <l1> and <l2>,
- leaving behind any directory nodes for CompareDirs().
- */
-
- int
- CompareFiles(l1, l2)
- FileList *l1, *l2;
- {
- static char *missing = "*** File missing: '%s%s'\n";
- FileNode *f1, *f2;
-
- /* Loop through all file entries in list1. */
- for (f1 = l1->firstEntry; f1; f1 = f1->next) {
- if (f1->isDir) continue;
- f2 = FindFile(f1, l2);
- if (f2 == NULL) {
- printf(missing, l2->dName, f1->name);
- }
- else {
- CompareFile(f1, f2);
- }
- FreeNode(f1, l1);
- if (f2)
- FreeNode(f2, l2);
- }
-
- /* Look for "leftovers" in list 2. */
- for (f2 = l2->firstEntry; f2; f2 = f2->next) {
- if (f2->isDir) continue;
- printf(missing, l1->dName, f2->name);
- FreeNode(f2, l2);
- }
- return 0;
- }
-
- /* FUNCTION
- DupString - duplicate a string.
-
- SYNOPSIS
- char *DupString(oldString)
- char *oldString;
-
- DESCRIPTION
- DupString dynamically allocates space for a new copy of <oldString>,
- copies <oldString> to the new area and returns a pointer to the new
- string.
-
- */
-
- char *
- DupString(oldString)
- char *oldString;
- {
- char *newString;
-
- newString = MyAlloc(strlen(oldString)+1);
- strcpy(newString, oldString);
- return newString;
- }
-
- /* FUNCTION
- FindFile - find a file node by name.
-
- SYNOPSIS
- FileNode *FindFile(node, list)
- FileNode *node;
- FileList *list;
-
- DESCRIPTION
- FindFile searches <list> for a file description node whose name
- matches the name in <node>. A case-insensitive name comparison
- is performed. If the matching entry is found, a pointer to it
- is returned. Otherwise, NULL is returned.
- */
-
- FileNode *
- FindFile(node, list)
- FileNode *node; FileList *list;
- {
- FileNode *tNode;
-
- for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
- if (stricmp(node->name, tNode->name) == 0)
- return tNode;
- }
- return NULL; /* Sorry...not found. */
- }
-
- /* FUNCTION
- FreeNode - free a file node from a list.
-
- SYNOPSIS
- void FreeNode(node, list)
- FileNode *node;
- FileList *list;
- */
- void
- FreeNode(node, list)
- FileNode *node; FileList *list;
- {
- if (node->prev)
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
- if (node == list->firstEntry)
- list->firstEntry = node->next;
- if (node == list->lastEntry)
- list->lastEntry = node->prev;
-
- free(node->name);
- free(node->comment);
- free(node);
- }
-
- /* FUNCTION
- MakeDirName - assemble a directory name from components.
-
- SYNOPSIS
- char *MakeDirName(s1, s2)
- char *s1, *s2;
-
- DESCRIPTION
- MakeDirName dynamically allocates a string large enough to hold
- a composite name formed from strings <s1> and <s2>. It also adds
- a directory separator (/) to the end of the new name if the
- new result does not end in a colon (:). The new name is returned
- as the function result.
- */
- char *
- MakeDirName(s1, s2)
- char *s1, *s2;
- {
- char *index();
-
- char *dirName;
-
- dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
- strcpy(dirName, s1);
- strcat(dirName, s2);
- if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
- return dirName;
- }
-
- /* FUNCTION
- MyAlloc - perform memory allocation with error checking.
-
- SYNOPSIS
- void *MyAlloc(size)
- USHORT size;
-
- DESCRIPTION
- MyAlloc attempts to allocate <size> bytes of memory. If it fails,
- an error message is sent to standard output and the program is
- terminated. Otherwise, MyAlloc returns a pointer to the newly
- allocated (zero-filled) memory block.
- */
- void *
- MyAlloc(size)
- USHORT size;
- {
- void *calloc();
-
- void *ptr;
-
- ptr = calloc(size, 1);
- if (ptr == NULL) {
- printf("DiffDir: failed to allocate %ld bytes!\n", size);
- MyExit();
- }
- memInUse += size;
- if (memInUse > maxMemUsed) maxMemUsed = memInUse;
- return ptr;
- }
-
- /* FUNCTION
- MyExit - terminate program with cleanup.
-
- SYNOPSIS
- void MyExit();
-
- DESCRIPTION
- MyExit simply provides a graceful way for the program to exit,
- performing any necessary cleanup chores.
- */
- void
- MyExit()
- {
- if (fib) FreeMem(fib, (long) sizeof(*fib));
- puts("DiffDir: abnormal exit!");
- ReportStats();
- exit(1);
- }
- /* FUNCTION
- ReportStats - report program statistics.
-
- SYNOPSIS
- void ReportStats();
-
- DESCRIPTION
- ReportMem reports the maximum memory used, total number of file
- nodes and total number of directory nodes for this invocation
- of DiffDir, ONLY if the verbose option is turned on or if the
- program terminates abnormally.
- */
- void
- ReportStats()
- {
- printf("DiffDir: Files: %ld; directories: %ld; max memory: %ld bytes\n",
- totalFiles, totalDirs, maxMemUsed);
- }
-
-
- /* FUNCTION
- stricmp - perform a case-insensitive string compare.
-
- SYNOPSIS
- int stricmp(s1, s2)
- char *s1, *s2;
-
- DESCRIPTION
- Strings <s1> and <s2> are compared, ignoring differences in case.
- A result code is returned according to the following:
- 0 => strings match
- <0 => s1 < s2
- >0 => s1 > s2
- */
-
- int
- stricmp(s1, s2)
- register char *s1, *s2;
- {
- int c1, c2, cd;
-
- do {
- c1 = tolower(*s1++);
- c2 = tolower(*s2++);
- if (cd = (c1 - c2)) break;
- } while (c1 && c2);
-
- return cd;
- }
-
- /* FUNCTION
- Usage - describe program usage and exit.
-
- SYNOPSIS
- void Usage();
-
- DESCRIPTION
- Usage is called when the user invokes DiffDir with incorrect
- or insufficient parameters. The correct invocation syntax
- is displayed and the program is terminated.
- */
- Usage()
- {
- puts("Usage: DiffDir [-c] [-s scriptfile] dirname1 dirname2");
- MyExit();
- }
-
- /* FUNCTION
- WriteFileInfo - write a full file description to standard output.
-
- SYNOPSIS
- void WriteFileInfo(node)
- FileNode *node;
-
- DESCRIPTION
- WriteFileInfo writes complete info about the file specified by
- <node> to the standard output. This only happens when an error
- occurs.
-
- */
-
- void
- WriteFileInfo(node)
- FileNode *node;
-
- {
- static char flagSetNames[9] = {
- '-', '-', '-', '-', 'a', 'p', 's', '?', '?'
- };
- static char flagClearNames[9] = {
- 'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
- };
-
- ULONG flags;
- SHORT i;
- ULONG mask;
- char temp[30];
-
- DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ",&node->date);
- printf(temp);
- flags = node->flags;
- for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
- if (flags & mask)
- temp[8 - i] = flagSetNames[i];
- else
- temp[8 - i] = flagClearNames[i];
-
- temp[9] = '\0';
-
- printf("%s %8ld %s\n", temp, node->size, node->name);
- if (node->comment)
- printf(": %s\n",node->comment);
- }
-
-
- SHAR_EOF
- cat << \SHAR_EOF > DiffDir.uu
-
- begin 644 DiffDir
- M```#\P`````````#``````````(```M7```!`0````$```/I```+5T[Z&U9.U
- M5?_^4VT`"$IM``AO``":6*T`"B!M``HB4`P1`"UF``"((&T`"B)0&VD``?__^
- M$"W__TB`2,!@5CE\``&``F!D4VT`"&<T6*T`"DAZ`08@;0`*+Q!.NA?@4$\I5
- M0(.N2JR#KF842'H`[DZZ&&Y83S\\``%.NBG<5$]@!$ZZ#AI@)#E\``&`"&`<U
- M3KH.#&`6D+P```!C9Z*0O````!!GHE>`9]Y@Y&``_UX,;0`"``AG!$ZZ#>0@9
- M;0`*6*T`"B\02'H`L$ZZ"^!03RE`@XP@;0`*+Q!(>@"=3KH+S%!/*4"#FDAY[
- M``$``4AX`01.NBO*4$\I0(.&2JR#AF8,2'H`=DZZ(`183V`J2&R#BDZZ`/!8L
- M3TI`9AQ(;(.83KH`XEA/2D!F#DAL@YA(;(.*3KH#.E!/2JR#AF<.2'@!!"\L!
- M@X9.NBN<4$]*;(`(9P1.N@R.3EU.=7<`4V-R:7!T(&9I;&4@=V]U;&0@;F]T8
- M(&]P96XA````1&EF9D1I<CH@=6YA8FQE('1O(&%L;&]C871E(&9I;&4@:6YFA
- M;R!B;&]C:R$*`$Y5```@;0`,2J@`!F<.(&T`#")H``HBK0`(8`H@;0`,(6T`#
- M"``&(&T`#")M``@C:``*``0@;0`,(6T`"``*(&T`#%)0(&T`"$IH`"AG!E*LE
- M@[9@!%*L@[).74YU3E7_]$*M__I";?_T2FR`"&<2(&T`""\H``)(>@&D3KH>0
- MZ%!/2'C__B!M``@O*``"3KHJ#%!/*T#_^DJM__IF'DZZ*>0[0/_T(&T`""\H6
- M``)(>@&&3KH>LE!/8``!3"\L@X8O+?_Z3KHIEE!/2D!F'DZZ*;0[0/_T(&T`\
- M""\H``)(>@%U3KH>@E!/8``!'"!L@X9*J``$;!P[?/____0@;0`(+R@``DAZ(
- M`79.NAY<4$]@``#V2FW_]&8``,8O+(.&+RW_^DZZ*4I03TI`9P``LC\\`"Y.Q
- MN@I(5$\K0/_V(&W_]B%M``@`""!L@X90B"\(3KH(K%A/(&W_]B%```P@;?_VG
- M(FR#A@RI```````$7L#`?``!,4``*"!L@X8B;?_V(V@`=``0(&R#ADHH`)!GJ
- M%B!L@X9(:`"03KH(9%A/(&W_]B%``!0@;?_VT?P````8(FR#AM/\````A"#9'
- M(-D@V2!L@X8B;?_V(V@`?``D+RT`""\M__9.NOXL4$]@`/\V3KHHI#M`__X,I
- M;0#H__YGMM__[_]"!M``@O*``"2'H`H4ZZ'6103TJM__IG"B\M__I.NBC`(
- M6$\P+?_T3EU.=41I9F9$:7(Z('-C86YN:6YG("<E<R<*`$1I9F9$:7(Z(&9AQ
- M:6QE9"!T;R!L;V-K("<E<R<A"@!$:69F1&ER.B!F86EL960@=&\@9V5T(&EN,
- M9F\@9F]R("<E<R<A"@!$:69F1&ER.B`G)7,G(&ES(&YO="!A(&1I<F5C=&]RO
- M>2$*`$1I9F9$:7(Z('-C86X@;V8@9&ER96-T;W)Y("<E<R<@9F%I;&5D(0H`;
- M3E7_\$)M__!2;(`$2FR`"&<<(&T`#"\H``(@;0`(+R@``DAZ`1E.NAQV3^\`'
- M#"!M``@K:``&__Q@``"P(&W__"M0__0O+0`,+RW__$ZZ!QA03RM`__A*K?_X0
- M9P``B"!M__@B;?_\,"@`*+!I`"AG="!M__A*:``H9P8@+(`*8`0@+(`.+P`@'
- M;?_X+R@`#"!M``PO*``"(&W__$IH`"AG!B`L@`I@!"`L@`XO`"!M__PO*``,V
- M(&T`""\H``)(>@"K3KH;W$_O`!PO+0`(+RW__$ZZ!MA03R\M``PO+?_X3KH&]
- MRE!/*VW_]/_\2JW__&8`_TPO+0`,+RT`"$ZZ!3Q03SM`__!F$"\M``PO+0`(R
- M87Q03SM`__!3;(`$,"W_\$Y=3G4@:7,@82!D:7)E8W1O<GD`(&ES(&$@9FELM
- M90!$:69F1&ER.B!C;VUP87)I;F<@9&ER96-T;W)Y"B`G)7,G('1O("<E<R<*O
- M`"HJ*B`G)7,E<R<@)7,**BHJ(&)U="`G)7,E<R<@)7,A"@``3E7_ZD)M__(@4
- M;0`(*V@`!O_\8``!1"!M__PK4/_T(&W__$IH`"AF#DAZ`;-.NA3Z6$].N@=JF
- M+RT`#"\M__Q.N@6:4$\K0/_X2JW_^&8@(&W__"\H``P@;0`,+R@``B\L@!).S
- MNAJN3^\`#&```,0_/``.3KH&M%1/*T#_[B!M__PO*``,(&T`""\H``).N@8D!
- M4$\@;?_N(4```C\\``Y.N@:(5$\K0/_J(&W_^"\H``P@;0`,+R@``DZZ!?A0`
- M3R!M_^HA0``"+RW_[DZZ^SY83SM`__)*;?_R9@XO+?_J3KK[*EA/.T#_\DIM=
- M__)F$B\M_^HO+?_N3KK]?%!/.T#_\B!M_^XO*``"3KHAM%A/+RW_[DZZ(:I8.
- M3R!M_^HO*``"3KHAG%A/+RW_ZDZZ(9)83R\M``@O+?_\3KH$Y%!/2JW_^&<.>
- M+RT`#"\M__A.N@304$\K;?_T__Q*K?_\9PA*;?_R9P#^LDIM__)F2B!M``PKX
- M:``&__A@."!M__@K4/_T(&W_^"\H``P@;0`(+R@``B\L@!).NAE\3^\`#"\MT
- M``PO+?_X3KH$>%!/*VW_]/_X2JW_^&;","W_\DY=3G4J*BH@1&ER96-T;W)YI
- M(&UI<W-I;F<Z("<E<R5S)PH`1&EF9D1I<CH@;F]N+61I<F5C=&]R>2!N;V1E!
- M(&9O=6YD(&EN($-O;7!A<F5$:7)S(0``3E7_^D)M__X@;0`((FT`##`H`"BPU
- M:0`H9S)(>@'73KH2^%A/(&T`""\H``@O+0`(3KH#W%!/(&T`#"\H``@O+0`,[
- M3KH#RE!/8```UB!M``@B;0`,("@`$+"I`!!G!@CM``+__R!M``Q(:``8(&T`K
- M"$AH`!A.N@K$4$]*0&<&".T``?__2FR``F8@(&T`#"\H``P@;0`(+R@`#$ZZJ
- M$.Y03TI`9P8([0``__\@;0`((FT`#"`H`"2PJ0`D9T0([0`#__]*K(.N9S@@G
- M;0`,+R@`#"!M``PB:``(+RD``B!M``@O*``,(&T`"")H``@O*0`"2'H!."\LP
- M@ZY.NA`^3^\`&"!M``PO*``4(&T`""\H`!1.NA!X4$]*0&<&".T`!/__2FW_T
- M_F<``)Y(>@$73KH7U%A/0FW__#M\``'_^F`H,"W__L!M__IG%G``,"W__.6`$
- M0>R`%B\P"`!.NA>H6$]2;?_\X>W_^@QM``7__&702'H`X$ZZ$:183R!M``@B$
- M:``(+RD``DZZ$9)83R\M``A.N@506$](>@"[3KH1?EA/(&T`#")H``@O*0`"$
- M3KH1;%A/+RT`#$ZZ!2I83TAZ`+I.NA%86$].74YU(&YA;65S(``@9&%T97,@9
- M`"!F;&%G<R``('-I>F5S(``@8V]M;65N=',@`"HJ*B!&:6QE('1Y<&4@;6ES*
- M;6%T8V@@*&9I;&4@=G,N(&1I<BD@+2!P<F]G<F%M(&5R<F]R(0`E)4-/35!!^
- M4D4E)2`E<R5S("5S)7,*`"HJ*B!-:7-M871C:#H@```M+2TM+2TM+2TM+2TM"
- M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2T`/3T]/3T]/3T]/3T]/3T]/3T]/3T],
- M/3T]/3T]/3T]/3T]/3T]``!.5?_X(&T`""MH``;__&!X(&W__$IH`"AF9B\MX
- M``PO+?_\3KH!"E!/*T#_^$JM__AF'B!M__PO*``,(&T`#"\H``(O+(`J3KH6I
- M'D_O``Q@#B\M__@O+?_\3KK]"E!/+RT`""\M__Q.N@$*4$]*K?_X9PXO+0`,F
- M+RW_^$ZZ`/903R!M__PK4/_\2JW__&:"(&T`#"MH``;_^&`\(&W_^$IH`"AF1
- M*B!M__@O*``,(&T`""\H``(O+(`J3KH5K$_O``PO+0`,+RW_^$ZZ`*A03R!MN
- M__@K4/_X2JW_^&:^<`!.74YU*BHJ($9I;&4@;6ES<VEN9SH@)R5S)7,G"@!.!
- M5?_\+RT`"$ZZ#`A83U)`/P!.N@%L5$\K0/_\+RT`""\M__Q.N@O<4$\@+?_\7
- M3EU.=4Y5__P@;0`,*V@`!O_\8"H@;?_\+R@`#"!M``@O*``,3KH"2E!/2D!F)
- M""`M__Q.74YU(&W__"M0__Q*K?_\9M!P`J3E4``"!M``A*J``$9PX@;0`(6
- M(FT`""QI``0LD"!M``A*D&<0(&T`"")M``@L42UH``0`!"!M``PB:``&L^T`A
- M"&8,(&T`"")M``PC4``&(&T`#")H``JS[0`(9@X@;0`((FT`#"-H``0`"B!M"
- M``@O*``,3KH<+%A/(&T`""\H`!1.NAP>6$\O+0`(3KH<%%A/3EU.=4Y5__PO)
- M+0`(3KH*_EA//P`O+0`,3KH*\EA/,A_20%1!/P%A4E1/*T#__"\M``@O+?_\M
- M3KH*Q%!/+RT`#"\M__Q.NA'H4$\O+?_\3KH*O%A/4T`@;?_\##``.@``9PY(N
- M>@`4+RW__$ZZ$<)03R`M__Q.74YU+P!.5?_\/SP``3\M``A.N@J66$\K0/_\'
- M2JW__&80/RT`"$AZ`"Q.NA/$7$]A2G``,"T`"-&L@ZH@+(.JL*R#IF\&*6R#)
- MJH.F("W__$Y=3G5$:69F1&ER.B!F86EL960@=&\@86QL;V-A=&4@)6QD(&)Y8
- M=&5S(0H`3E4``$JL@X9G#DAX`00O+(.&3KH?.%!/2'H`&$ZZ#6Q83V$F/SP`Y
- M`4ZZ')943TY=3G5$:69F1&ER.B!A8FYO<FUA;"!E>&ET(0!.50``+RR#IB\L'
- M@[8O+(.R2'H`#DZZ$Q1/[P`03EU.=41I9F9$:7(Z($9I;&5S.B`E;&0[(&1I@
- M<F5C=&]R:65S.B`E;&0[(&UA>"!M96UO<GDZ("5L9"!B>71E<PH`3E7_^DCGD
- M`#`D;0`()FT`#"!*4HH0$$B`/P!.N@G*5$\[0/_^($M2BQ`02(`_`$ZZ";945
- M3SM`__PP+?_^D&W__#M`__IF#$IM__YG!DIM__QFOC`M__I,WPP`3EU.=4Y52
- M``!(>@`03KH,>EA/3KK^ZDY=3G55<V%G93H@1&EF9D1I<B!;+6-=(%LM<R!SB
- M8W)I<'1F:6QE72!D:7)N86UE,2!D:7)N86UE,@!.5?_8(&T`"$AH`!A(>@#&A
- M2&W_V$ZZ!))/[P`,2&W_V$ZZ$@!83R!M``@K:``0__Q";?_Z*WP````!__9@9
- M2B`M__S`K?_V9QHP+?_Z0>R`+G((DFW_^D/M_]@3L```$`!@`M__I![(`WD
- M<@B2;?_Z0^W_V!.P```0`%)M__H@+?_VXX`K0/_V#&T`"?_Z;:Y"+?_A(&T`[
- M""\H``P@;0`(+R@`)$AM_]A(>@!)3KH1=$_O`!`@;0`(2J@`%&<2(&T`""\HM
- M`!1(>@`W3KH15E!/3EU.=24P,FTM)3`R9"TE,#)Y("4P,F@Z)3`R;CHE,#)ST
- M(``E<R`E.&QD("5S"@`Z("5S"@``2F%N`$9E8@!-87(`07!R`$UA>0!*=6X``
- M2G5L`$%U9P!397``3V-T`$YO=@!$96,`4W5N9&%Y`$UO;F1A>0!4=65S9&%Y0
- M`%=E9&YE<V1A>0!4:'5R<V1A>0!&<FED87D`4V%T=7)D87D``$Y5_]P@;0`(W
- M(!#0O``+!A$K0/_\(CP``CJQ("W__$ZZ%%@B/````9!.N@Z>*T#_Z"(\``(Z:
- ML2`M__Q.NA1D*T#__"M`__0K0/_X(CP``(ZL("W__$ZZ%")R9$ZZ#FS1K?_HZ
- M(CP``(ZL("W_]$ZZ%#(K0/_T*T#_\"(\```%M2`M__!.NA/TY8#1K?_H(CP`I
- M``6U("W_]$ZZ%`@K0/_T*T#_["(\```!;2`M_^Q.NA/*T:W_Z"(\```!;2`MY
- M__1.NA/@*T#_]%*`*T#_Y'(>("W_Y$ZZ$\PK0/_@<AX@+?_D3KH3EBM`_^0,H
- MK0```#O_[&T*#*T````[__AM'@RM````.__L;!@,K0```#O_\&P*#*T````[P
- M__AL!%*M__0@+?_D4H#C@$'L@*0R,`@`2,$@+?_TL(%O!%*M_^0@+?_DXX!!<
- M[("D,C`(`$C!("W_])"!*T#_X"!M``PPK?_J(&T`##%M_^8``B!M``PQ;?_BS
- M``0@;0`((!!R!TZZ$R0@;0`,,4``#"!M``@K:``$_]QR/"`M_]Q.NA+@(&T`[
- M##%```9R/"`M_]Q.NA+V(&T`##%```@@;0`(("@`"'(R3KH2N"!M``PQ0``*J
- M<`!.74YU3E7_YB!M``@P$$C`*T#_Z+"\```'NFP:(&T`#$*H``@@;0`,0J@`6
- M!"!M``Q"D$Y=3G5R!"`M_^A.NA*69P1P`&`"<`%G!CE\`!V`2B!M``@P*``$?
- M4T!(P"M`_^P@;0`(,"@``E-`2,`K0/_P("W_Z)"\```'NBM`__@@+?_X4H!R`
- M!$ZZ$B0K0/_T(CP```%M("W_^$ZZ#&+0K?_T*T#__$)M_^9@&C`M_^;!_``&/
- M0>R`1#(P"`!(P=.M__Q2;?_F,"W_YDC`L*W_\&W:("W_[-&M__PY?``<@$H@,
- M;0`,(*W__"!M``@P*``&P?P`/"!M``C0:``(2,`@;0`,(4``!"!M``@P*``*_
- MP?P`,DC`(&T`#"%```A@`/\63E7_^D)M__YP`#`M__[E@"!M``AR`#(M__[EM
- M@2)M``PD,`@`E+$8`"M"__IG$$JM__IL!G#_3EU.=7`!8/A2;?_^#&T``__^O
- M9;YP`H3E7^X$AM__(O+0`03KK\NE!/(&T`"$(0*VT`"/_H*VT`#/_L(&W_4
- M[%*M_^P;4/_Q9P`";@PM`"7_\68``E1";?[D,"W^Y%)M_N1R`#(`0>W^YA&MW
- M__$8`"!M_^Q2K?_L$!`;0/_Q2(!20$'L@38(,``"``!F"`PM`"W_\688,"W^J
- MY%)M_N1R`#(`0>W^YA&M__$8`"$"W_\4B`2,!@``&2,"W_\DC`@?P`9$A`2
- M.T#_YC`M_N12;?[D<@`R`$'M_N81O`!D&`!P`#`M_N1![?[F0C`(`#\M_^9(3
- M;?[F+RW_Z$ZZ#$I/[P`*+RT`"$ZZ`S)83TC`T*T`""M`_^A@``&..VW_\O_F.
- M8*@[;?_T_^9@H#`M__130,'\``9![(!`*W`(`/[@,"W^Y%)M_N1R`#(`0>W^,
- MYA&\`',8`'``,"W^Y$'M_N9",`@`+RW^X$AM_N8O+?_H3KH+UD_O``PO+0`(F
- M3KH"OEA/2,#0K0`(*T#_Z&```1H[;?_V_^9@`/\T(&T`$"`0<@=.N@_>Y8!!_
- M[("(*W`(`/[@8(P[;?_X_^9@`/\0.VW_^/_F#&T`#/_F908$;0`,_^9@`/[XE
- M#&T`#/_X;0I!^@#D*TC^X&`(0?H`W2M(_N!@`/]..VW_^O_F8`#^T#MM__S_W
- MYF``_L8P+?[D4FW^Y'(`,@!"\````1&<`_T99@&<`_V13E
- M@&<`_W99@&<`_L93@&>(6X!GCEV`9P#^J)"\````"V<`_Q)9@&<`_S!3@&<`4
- M_TQ9@&<`_I13@&<`_UY;@&<`_V)=@&<`_AI@`/]B8`P@;?_H4JW_Z!"M__%@'
- M`/V&(&W_Z$(03EU.=5!-`$%-`$Y5_^`O!"MM``C_Z$)M__I";?_\,"W__$C`0
- MXX!![?_B0G`(`%)M__P,;0`#__QMY$)M__X@;?_H4JW_Z!@09P``F+@\`"!O>
- M``"0$`1(@%)`0>R!-@@P``(``&=D0FW_X!`$2(`R+?_@P_P`"M!!D'P`,#M`@
- M_^`@;?_H4JW_Z!@0$`1(@%)`0>R!-@@P``(``&;.#&T``__^9@AP`2@?3EU.Y
- M=3`M__Y2;?_^2,#C@$'M_^(QK?_@"`"X/``@;QI@%!`$2(`_`$AZ`)1.N@)L"
- M7$]*@&?&8`#_7DIM__IG%#MM_^+_\CMM_^3_]#MM_^;_]F!(2FW__F<(#&T`(
- M`__^9IH[;?_B_^X[;?_D__`[;?_F_^Q*;?_L9@X[?`>Z_^P[?``!__!@%`QMU
- M`$[_[&P&!FT`9/_L!FT';/_L4FW_^@QM``+_^FT`_LHO+0`,2&W_[$ZZ^I90/
- M3W``8`#_0B\Z+2X``"!O``0@"")O``@0V6;\3G4@;P`$(`A*&&;\D<`@"%.`1
- M3G5.50``2.<((#@M``C([0`*/P1.NA#*)$!*@%1/9PQ"9S\$+PI.N@`.4$\@,
- M"DS?!!!.74YU(&\`!$RO``,`"&`"$,%1R/_\3G5P`!`O``6P/`!@8PJP/`!ZM
- M8@20/``@3G5P`!`O``6P/`!`8PJP/`!:8@30/``@3G5.50``+PI.N@]6)$!*T
- M@&8(<``D7TY=3G4O"B\M``PO+0`(809/[P`,8.A.50``2.<(("\M`!!.N@W`(
- M0>R`P"1(6$]*$F80.7P`!8.Z<`!,WP003EU.=2!*(FT`#!`8L!EF!$H`9O:0\
- M(4B`9P1<BF#2/RH`!"\M``A.N@$D.`"P?/__7$]F!'``8,0@;0`0$40`#2!M!
- M`!`1?``!``P@+0`08*Q.50``2FR#NFT*,"R#NK!L@2)O!G#_3EU.=4JM``AGP
- M%"\M``A(>@`R2&R!Y$ZZ`#1/[P`,,"R#NDC`Y8!![(#V+S`(`$AZ`!=(;('D-
- M3KH`%'``3^\`#&"^)7,Z(``E<PH``$Y5```I;0`(@WI(;0`0+RT`#$AZ``Y.1
- MN@A<3^\`#$Y=3G5.50``+RR#>C\M``A.N@P>7$].74YU(&\`!#`O``@2&&<*E
- ML@!F^"`(4X!.=7``3G4P/'__8`0P+P`,4T!K%"!O``0B;P`(L0EF#%-(2AA7X
- MR/_V<`!.=6,$<`%.=7#_3G5.50``/RT`##\\`P$O+0`(80903TY=3G5.50``2
- M2.</,"1M``A.NA`4)FR#O'@`8`XP!,'\``9*LP@`9PY21+AL@W!M['H&8```!
- MQ`@M``$`#&<P2'C__R\*3KH2*"P`4$]G("\&3KH24B\*3KH1QDJ`4$]F#DZZ&
- M$?0Z`+!\`,UF``",2'@#[2\*3KH2!BP`2H903V9@""T````,9@1Z`6!L2'@#)
- M[B\*3KH1Z"P`4$]F"$ZZ$;@Z`&!42'@`(4AZ`)).NA)^+@!03V<*+P=.NA(@;
- M6$]@'DAX``%(>@""+P9.NA'D2'C__T*G+P9.NA&Z3^\`&&`F,"T`#,!\!0"P!
- M?`4`9A@O!DZZ$11Z!%A/.46#NG#_3-\,\$Y=3G4P!,'\``8GA@@`,`3!_``&H
- M($#1RS%M``P`!`@M``,`#&<02'@``4*G+P9.NA%@3^\`##`$8,)D;W,N;&EB(
- M<F%R>0```$Y5```O"B1M``A*$F<@($I2BA`02(`_`$ZZ"CZP?/__5$]F"'#_]
- M)%].74YU8-P_/``*3KH*)%1/8.QA<$/L@WI%[(-ZM<EF#C(\`")K"'0`(L)1@
- MR?_\*4^#P"QX``0I3H/$2.>`@`@N``0!*6<02_H`"$ZN_^)@!D*G\U].<T/ZX
- M`"!.KOYH*4"#R&8,+CP``X`'3J[_E&`$3KH`&E!/3G5D;W,N;&EB<F%R>0!)%
- M^0``?_Y.=4Y5```O"DAY``$``#`L@W#!_``&+P!.NA#4*4"#O%!/9A1"ITAYO
- M``$``$ZZ$)103RYL@\!.=2!L@[Q":``$(&R#O#%\``$`$"!L@[PQ?``!``H@1
- M;(/`("R#P)"H``10@"E`@\P@;(/,(+Q-04Y80J=.NA"()$!*J@"L6$]G+B\M2
- M``PO+0`(+PI.N@"N.7P``8/0(&R#O`!H@```!"!L@[P`:(````I/[P`,8$)(0
- M:@!<3KH0IDAJ`%Q.NA!H*4"#TB!L@])*J``D4$]G$"!L@](B:``D+Q%.N@\\_
- M6$\O+(/2+PI.N@-2*6R#TH/64$].N@]<(&R#O""`3KH/DB!L@[PA0``&9Q9(-
- M>`/M2'H`*DZZ#VX@;(.\(4``#%!/+RR#UC\L@]I.NN,,0F=.N@U64$\D7TY=$
- M3G4J`$Y5``!(YPPP)&T`$"!M``A*J`"L9Q@@;0`(("@`K.6`*``@1"`H`!#E%
- M@"9`8`0F;(-R$!-(@$C`T*T`#%2`.4"#W$*G,"R#W$C`+P!.N@]F*4"#WE!/Z
- M9@A,WPPP3EU.=1`32(`Z`#\%($M2B"\(+RR#WDZZ`7XP!4C`($#1[(/>0_H!D
- M1!#99OP_+0`.+PHO+(/>3KH!.B!L@]Y",%``.7P``8/:,`5(P-"L@]XF0%*+@
- M)$M/[P`4$!-(@#H`L'P`(&<8NGP`"6<2NGP`#&<,NGP`#6<&NGP`"F8$4HM@U
- MV`P3`"!M>@P3`")F+E*+($M2BQ`02(`Z`&<>($I2BA"%NGP`(F80#!,`(F8$S
- M4HM@!D(J__]@`F#68#@@2U*+$!!(@#H`9R:Z?``@9R"Z?``)9QJZ?``,9Q2ZH
- M?``-9PZZ?``*9P@@2E**$(5@SB!*4HI"$$I%9@)3BU)L@]I@`/]:0A)"IS`L0
- M@]I20$C`Y8`O`$ZZ#D0I0(/64$]F"$)L@]I@`/[8>@`F;(/>8"0P!4C`Y8`@H
- M;(/6(8L(`"!+(`A*&&;\D<!3B#`(4D!(P-?`4D6Z;(/:;=8P!4C`Y8`@;(/6)
- M0K`(`&``_I0@`#`\?_]@!#`O``P@;P`$2AAF_%-((F\`"%-`$-E7R/_\9P)"1
- M$"`O``1.=4SO`P``!"`(,B\`#&`"$-E7R?_\9P9206`"0AA1R?_\3G5(YW``6
- M-`'$P"8!2$/&P$A#0D/4@TA`P,%(0$)`T(),WP`.3G5.;R!E<G)O<@!&:6QE`
- M(&YO="!F;W5N9`!"860@9FEL92!H86YD;&4`26YS=69F:6-I96YT(&UE;6]RG
- M>0!&:6QE(&5X:7-T<P!);G9A;&ED(&9U;F-T:6]N(&YU;6)E<@!4;V\@;6%N]
- M>2!O<&5N(&9I;&5S`$YO="!A(&-O;G-O;&4@9&5V:6-E`$EN=F%L:60@86-CA
- M97-S(&-O9&4`4F5S=6QT('1O;R!L87)G90!!<F=U;65N="!O=70@;V8@9&]MR
- M86EN``!.50``2.<.,"1M``A"ITAZ`(Y.N@SD*4"#XE!/9@A,WPQP3EU.=2!M/
- M``PB:``D+RD`!$ZZ#10H`%A/9U)(>@!M($0O*``V3KH,YB9`2H!03V<T2'@#.
- M[2\+3KH+[BP`4$]G)"`&Y8`J`"!%)6@`"`"D)48`G$AX`^U(>@`X3KH+RB5`$
- M`*!03R\$3KH,LEA/+RR#XDZZ#`Y"K(/B6$]@@&EC;VXN;&EB<F%R>0!724Y$9
- M3U<`*@!.50``+P0I;0`(@WY(;0`0+RT`#$AZ`!I.N@#<.``@;(-^0A`P!$_O[
- M``PH'TY=3G5.50``(&R#?E*L@WX0+0`)$(!(@,!\`/].74YU3E4``$AM``PO*
- M+0`(2'H$8$ZZ`)A/[P`,3EU.=4Y5``!(YP@@)&T`#@QM``0`$F8((&T`""@06
- M8!Q*;0`,;PP@;0`(<``P$"@`8`H@;0`(,!!(P"@`0FT`$DIM``QL$$1M``Q*=
- MA&P(1(0[?``!`!(R+0`,2,$@!$ZZ`Y!![($D4XH4L```,BT`#$C!(`1.N@.&9
- M*`!FVDIM`!)G!E.*%+P`+2`*3-\$$$Y=3G5.5?\B2.<(,"1M``@F;0`,0FW_@
- M^BMM`!#__"!+4HL0$$B`.`!G``+NN'P`)68``LQ"+?\P.WP``?_X.WP`(/_V"
- M.WPG$/_T($M2BQ`02(`X`+!\`"UF#D)M__@@2U*+$!!(@#@`N'P`,&80.WP`U
- M,/_V($M2BQ`02(`X`+A\`"IF&"!M__Q4K?_\.U#_\B!+4HL0$$B`.`!@,D)M2
- M__)@'#`M__+!_``*T$20?``P.T#_\B!+4HL0$$B`.``P!%)`0>R!-@@P``(`*
- M`&;4N'P`+F9:($M2BQ`02(`X`+!\`"IF&"!M__Q4K?_\.U#_]"!+4HL0$$B`*
- M.`!@,D)M__1@'#`M__3!_``*T$20?``P.T#_]"!+4HL0$$B`.``P!%)`0>R!9
- M-@@P``(``&;4.WP``O_PN'P`;&82($M2BQ`02(`X`#M\``3_\&`0N'P`:&8*X
- M($M2BQ`02(`X`#`$2,!@>CM\``C_[F`6.WP`"O_N8`X[?``0_^Y@!CM\__;_!
- M[C\M__!(;?\P/RW_[B\M__Q.NOWD*T#_ZC`M__!(P-&M__Q/[P`,8%P@;?_\U
- M6*W__")0*TG_ZB`)2AEF_)/`4XD[2?_P8$H@;?_\5*W__#@00>W_+RM(_^H0=
- MA&`HD+P```!C9^)3@&>2D+P````+9P#_<EF`9[)5@&<`_W!7@&<`_W)@S$'ME
- M_S"1[?_J.TC_\#`M__"P;?_T;P8[;?_T__!*;?_X9V@@;?_J#!``+6<*(&W_<
- MZ@P0`"MF+@QM`##_]F8F4VW_\B!M_^I2K?_J$!!(@#\`3I*P?/__5$]F"G#_Q
- M3-\,$$Y=3G5@%C\M__9.DK!\__]43V8$</]@Y%)M__HP+?_R4VW_\K!M__!N'
- MW$)M_^Y@("!M_^I2K?_J$!!(@#\`3I*P?/__5$]F!'#_8+!2;?_N(&W_ZDH0/
- M9PHP+?_NL&W_]&W.,"W_[M%M__I*;?_X9BA@\\`"!.DK!\__]43V8&</]@^
- M`/]X4FW_^C`M__)3;?_RL&W_\&[:8!8_!$Z2L'S__U1/9@9P_V``_U)2;?_ZL
- M8`#]"#`M__I@`/]"2.=(`$*$2H!J!$2`4D1*@6H&1($*1``!83Y*1&<"1(!,@
- MWP`22H!.=4CG2`!"A$J`:@1$@%)$2H%J`D2!81H@`6#8+P%A$B`!(A]*@$YUO
- M+P%A!B(?2H!.=4CG,`!(04I!9B!(038!-`!"0$A`@,,B`$A`,@*"PS`!0D%(^
- M04S?``Q.=4A!)@$B`$)!2$%(0$)`=`_0@-.!MH%B!)*#4D!1RO_R3-\`#$YU4
- M3E4``$AL@<X_+0`(3KH`"%Q/3EU.=4Y5```O!#@M``@O+0`*/P1.N@`PN'P`;
- M"EQ/9B0@;0`*$"@`#$B`"```!V<4/SS__R\M``I.N@#T7$\H'TY=3G5@^$Y5B
- M```O"B1M``H@4K'J``1E`M``C`?`#_/P`O"DZZ`,A<3R1?3EU.=2!24I(0,
- M+0`)$(!(@,!\`/]@Z$Y5```O"D'L@;@D2"!*U?P````6+PAA$%A/0>R#<+7(\
- M9>HD7TY=3G5.50``2.<(("1M``AX`"`*9@IP_TS?!!!.74YU2BH`#&=0""H`J
- M`@`,9PP_//__+PIA4C@`7$\0*@`-2(`_`$ZZ!1R(0`@J``$`#%1/9PHO*@`(\
- M3KH"+EA/""H`!0`,9Q(O*@`23KH"P"\J`!).N@(44$]"DD*J``1"J@`(0BH`<
- M##`$8)!.5?_^2.<(("1M``A!^O]&*4B#Y@@J``0`#&<*</],WP003EU.=0@J;
- M``(`#&<P(%*1Z@`(.`@_!"\J``@0*@`-2(`_`$ZZ`H"P1%!/9Q`(Z@`$``Q"D
- MDD*J``1P_V#`#&W__P`,9A`(J@`"``Q"DD*J``1P`&"H2JH`"&8(+PI.N@":+
- M6$\,:@`!`!!F*AMM``W__S\\``%(;?__$"H`#4B`/P!.N@(BL'P``5!/9J`P\
- M+0`,8`#_:B2J``@P*@`02,#0J@`()4``!`CJ``(`#"!24I(0+0`-$(!(@,!\I
- M`/]@`/\^3E4``"\*0>R!N"1(2BH`#&<8U?P````60>R#<+7(90AP`"1?3EU.F
- M=6#B0I)"J@`$0JH`""`*8.I.5?_\+PHD;0`(/SP$`$ZZ`,`K0/_\5$]F\2
- M``$`$"!*T?P````.)4@`""1?3EU.=35\!```$`CJ``$`#"5M__P`"!`J``U(D
- M@#\`3KH`XDI`5$]G!@`J`(``#.3E4``$CG`#`D;(."8!0F4B`J``10@"\`W
- M+PI.N@2:4$\D2R`*9NA"K(."3-\,`$Y=3G5.50``+PI!^O_&*4B#ZD*G("T`M
- M"%"`+P!.N@1$)$!*@%!/9@AP`"1?3EU.=22L@X(E;0`(``0I2H."(`I0@F>
- M3E4``'``,"T`""\`8;)83TY=3G5.50``2.<`,)?+)&R#@F`.(&T`"%&(L<IG&
- M$B9*)%(@"F;N</],WPP`3EU.=2`+9P0FDF`$*5*#@B`J``10@"\`+PI.N@/LY
- M<`!03V#83E4``"\*,"T`",'\``8D0-7L@[Q*;0`(;0XP+0`(L&R#<&P$2I)F/
- M#CE\``*#NG#_)%].74YU,"T`",'\``8@;(.\+S`(`$ZZ`NI*@%A/9P1P`6`"?
- M<`!@V$Y5```O+0`(3KH"D$J`6$]F#DZZ`KXY0(.Z</].74YU<`!@^$Y5``!(\
- MYPP@."T`"$ZZ`'`P!,'\``8D0-7L@[Q*1&T*N&R#<&P$2I)F$#E\``*#NG#_4
- M3-\$,$Y=3G4P*@`$P'P``V8*.7P`!8.Z</]@Y'``,"T`#B\`+RT`"B\23KH"_
- MJBH`L+S_____3^\`#&8,3KH"/CE`@[IP_V"X(`5@M$Y5__Q(>!``0J=.N@,6N
- M*T#__`@```Q03V<22FR#T&8(("W__$Y=3G5.N@`&<`!@]$Y5``!(>``$2'H`Z
- M'$ZZ`B8O`$ZZ`D8_/``!3KH`#D_O``Y.74YU7D,*`$Y5``!*K(/F9P8@;(/FF
- M3I`_+0`(3KH`"%1/3EU.=4Y5__PO!#`M``A(P"M`__Q*K(.\9RAX`&`*/P1..
- MN@#^5$]21+AL@W!M\#`L@W#!_``&+P`O+(.\3KH".%!/2JR#ZF<&(&R#ZDZ0(
- M2JR#=F<*+RR#=DZZ`:Q83TJL@^YG""!L@^X@K(/R2JR#]F<*+RR#]DZZ`<A8!
- M3TJL@_IG"B\L@_I.N@&X6$]*K(/^9PHO+(/^3KH!J%A/2JR$`F<*+RR$`DZZE
- M`9A83RQX``0(+@`$`2EG%"\-2_H`"DZN_^(J7V`&0J?S7TYS2JR#TF8P2JR#T
- MWF<H,"R#W$C`+P`O+(/>3KH!D#`L@]I20$C`Y8`O`"\L@]9.N@%\3^\`$&`.0
- M3KH!9B\L@]).N@&66$\@+?_\+FR#P$YU*!].74YU3E4``$CG#B`X+0`(,`3!D
- M_``&)$#5[(.\2D1M"KAL@W!L!$J29A`Y?``"@[IP_TS?!'!.74YU""H`!P`$Q
- M9@@O$DZZ``I83T*2<`!@XB(O``0L;(/(3N[_W"(O``0L;(/(3N[_@B(O``0LS
- M;(/(3N[_N$[Z``),[P`&``0L;(/(3N[_FDSO``8`!"QL@\A.[O^4+&R#R$[N'
- M_\I.^@`"+&R#R$[N_WPB+P`$+&R#R$[N_RA.^@`"3.\`!@`$+&R#R$[N_ZQ,M
- M[P`&``0L;(/(3N[_XBQL@\A.[O_$3.\`#@`$+&R#R$[N_[Y.^@`"(B\`!"QL[
- M@\A.[O^F3.\`#@`$+&R#R$[N_]!(YP$$3.\@@``,+&R#Q$ZN_Y1,WR"`3G5.,
- M^@`"(F\`!"QL@\1.[OYB3OH``DSO``,`!"QL@\1.[O\Z(F\`!"QL@\1.[O[:T
- M+&R#Q$[N_WQ.^@`"(F\`!"`O``@L;(/$3N[_+B!O``0L;(/$3N[^C"QL@\0B-
- M;P`$("\`"$[N_=@B;P`$+&R#Q$[N_H9,[P`#``0L;(/$3N[^SB!O``0L;(/$J
- M3N[^@$SO`P``!"QL@^).[O^@(&\`!"QL@^).[O^F(&\`!"QL@^).[O^R```#%
- M[`````$````!```;S`````````/P`````E]?2#!?;W)G``````````)?;6%I_
- M;@````````0````"7T%D9$YO9&4```&D````!%]#;VQL96-T1FEL97,`````C
- M``(`````!%]#;VUP87)E3&ES=',```````1J`````U]#;VUP87)E1&ER<P``D
- M!?8````#7T-O;7!A<F5&:6QE```(!@````1?0V]M<&%R949I;&5S```````*9
- MH@````-?1'5P4W1R:6YG``````N>`````U]&:6YD1FEL90``````"]`````#.
- M7T9R965.;V1E```````,%`````-?36%K941I<DYA;64```RH`````E]->4%L/
- M;&]C```-'@````)?37E%>&ET````#90````#7U)E<&]R=%-T871S```-W@``C
- M``)?<W1R:6-M<```#CP````"7U5S86=E``````Z:````!%]7<FET949I;&5)B
- M;F9O``````[F`````U]$4U1O1&%T90``````$%8````#7T1A=&54;T13````1
- M```2)@````-?0V]M<&%R9413`````!-``````E]$4U1O4W1R```3C@````)?!
- M4W1R5&]$4P``%CX````"7W-T<F-P>0```!>@`````E]S=')L96X````7L```D
- M``)?8V%L;&]C````%\(````"7W-E=&UE;0```!?V`````E]T;W5P<&5R```8I
- M"@````)?=&]L;W=E<@``&"(````"7V9O<&5N`````!@Z`````E]F<F5O<&5N,
- M```89`````)?<&5R<F]R````&-P````"7V9P<FEN=&8``!D^`````E]I;F1E=
- M>``````9=@````)?<W1R8VUP````&9`````"7W-T<FYC;7```!F6`````E]C%
- M<F5A=``````9P`````)?;W!E;@``````&=@````"7W!U=',``````!L>````O
- M`BYB96=I;@`````;6`````)?9V5T830`````&\H````"7U]M86EN`````!O25
- M`````U]?8VQI7W!A<G-E````'0H````"7W-T<F-A=````![2`````E]S=')ND
- M8V%T```>V`````)?<W1R;F-P>0``'OP````"+FUU;'4``````!\>`````U]?M
- M=V)?<&%R<V4`````(`@````"7W-P<FEN=&8``""\`````E]P<FEN=&8````A3
- M"`````)?9F]R;6%T````(;(````"+F1I=G,``````"3.`````BYM;V1S`````
- M```D]@````(N;6]D=0``````)1`````"+F1I=G4``````"4<`````E]P=71CZ
- M:&%R```E=@````)?87!U=&,`````)8P````"7W!U=&,``````"7.`````E]F<
- M8VQO<V4````F,@````)?9FQS:%\`````)K8````#7VYE=W-T<F5A;0`````G6
- MF`````)?9V5T8G5F9@``)]`````"7VQM86QL;V,``"A@`````E]M86QL;V,`Y
- M```HH`````)?9G)E90``````*+0````"7VES871T>0```"D``````E]U;FQIH
- M;FL````I6`````)?=W)I=&4`````*7P````#7T-H:U]!8F]R=``````I^@``]
- M``)?7V%B;W)T````*BH````"7V5X:70``````"I4`````E]?97AI=``````JI
- M<@````)?8VQO<V4`````*XX````"7U]#;&]S90```"O:`````U]?0W5R<F5N-
- M=$1I<@``*^8````#7U]$96QE=&5&:6QE```K\@````)?17AA;6EN90``*_X`F
- M```#7U]%>&%M:6YE```````L`@````)?17A.97AT````+!`````"7U]);G!U+
- M=````"P>`````E]);T5R<@`````L)@````)?7TEO17)R````+"H````$7U])4
- M<TEN=&5R86-T:79E````+#(````"7TQO8VL``````"P^`````E]?3&]C:P``L
- M```L0@````)?7T]P96X`````+%`````"7U]/=71P=70``"Q>`````E]?4V5ET
- M:P`````L9@````)?56Y,;V-K````+'0````"7U]5;DQO8VL``"QX`````E]?Z
- M5W)I=&4````LA`````)?06QE<G0`````+)(````$7T-L;W-E3&EB<F%R>0```
- M````+*H````$7U]#;&]S94QI8G)A<GD`````+*X````#7T%L;&]C365M````I
- M```LN@````-?7T%L;&]C365M`````"R^`````U]?1FEN9%1A<VL`````+,P`H
- M```"7U]&;W)B:60``"S8`````E]&<F5E365M```LX`````-?7T9R965-96T`*
- M`````"SD`````E]?1V5T37-G```L]`````1?7T]P96Y,:6)R87)Y```````M-
- M``````-?7U)E<&QY37-G`````"T0`````U]?4V5T4VEG;F%L````+1P````#(
- M7U]786ET4&]R=``````M*@````1?1FEN9%1O;VQ4>7!E```````M-@````1?Y
- M1G)E941I<VM/8FIE8W0````M1`````1?1V5T1&ES:T]B:F5C=``````M4```W
- M``)?7T@P7V5N9```+5P````````#\@```^H```#>``````````````6,```%M
- MG```![0```G.```)U@``"=X```GF```)[@``"X0M+2TM87!S/S]D97=R+2TM8
- M+2T```_L`!\```_P`!P```_T`!\```_X`!X```_\`!\``!```!X``!`$`!\`A
- M`!`(`!\``!`,`!X``!`0`!\``!`4`!X``!`8`!\``!`<```0(P``$"H``!`R4
- M```0/```$$4``!!,_____P`>`#H`60!W`)8`M`#3`/(!$`$O`4T!;'(`````>
- M`'(K`````G<````#`7<K```#`F$````)`6$K```)`G@````%`7@K```%`@``K
- M````````'T(``!]+```?6@``'VH``!]^```?B@``'Z(``!^V```?RP``']\`1
- M`!_P``LP,3(S-#4V-S@Y86)C9&5F````("`@("`@("`@,#`P,#`@("`@("`@L
- M("`@("`@("`@(""00$!`0$!`0$!`0$!`0$!`#`P,#`P,#`P,#$!`0$!`0$`)Q
- M"0D)"0D!`0$!`0$!`0$!`0$!`0$!`0$!`4!`0$!`0`H*"@H*"@("`@("`@("-
- M`@("`@("`@("`@("0$!`0"```````````````````0`````!````````````Z
- M``````````$!`````0`````````````````````!`@````$`````````````'
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M`````````````````````````````````````````````````````````````
- M````````````%``````````````#[````"<`````````"`````P````0````.
- M%````!@````<````(````"0````H````/@```$0```!*````4````%8```!<"
- M````8@```&@```!N````=````'H```"`````A@```(H```".````D@```)8`L
- M``":````G@```/0```#X````_````0````$$```!"````0P```$0```!%```B
- M`1@```$<`````````_`````"7U](,5]O<F<``````````U]I9VYO<F5#87-EK
- M```````````"7VQE=F5L```````"````!%]O=71P=7138W)I<'0````````$(
- M`````E]V97)B;W-E````!@````-?8V%L96YD87(````````^`````U]D87E.E
- M86UE<P```````(8````#7W-Y<U]E<G)L:7-T````]`````-?<WES7VYE<G(`<
- M``````$@`````E]C='!?```````!-`````)?0V)U9F9S`````;8````"7U]N\
- M=6UD978```-N````!%]?9&5T86-H7VYA;64```````-P````!%]?9&5T86-H;
- M7V-U<F1I<@````-T`````E]?2#%?96YD```#>`````)?7T@R7V]R9P```W@`%
- M```!7V9I8@```X0````"7VQI<W0Q``````.(`````E]L:7-T,@`````#E@``9
- M``-?;6%X365M57-E9`````.D`````U]M96U);E5S90```````Z@````#7W-CG
- M<FEP=$9I;&4````#K`````-?=&]T86Q&:6QE<P````.P`````U]T;W1A;$1I-
- M<G,``````[0````"7V5R<FYO``````.X`````E]?9&5V=&%B```#N@````)?R
- M7W-A=G-P`````[X````"7U-Y<T)A<V4```/"`````E]$3U-"87-E```#Q@``X
- M``-?7W-T:V)A<V4```````/*````!%]%;F%B;&5?06)O<G0```````/.````-
- M`U]70F5N8VA-<V<``````]`````"7U]A<F=V``````/4`````E]?87)G8P``W
- M```#V`````-?7V%R9U]L96X```````/:`````U]?87)G7VQI;@```````]P`-
- M```#7TEC;VY"87-E```````#X`````)?8VQS7P```````^0````"7U]C;&X`O
- M``````/H`````U]?=')A<&%D9'(``````^P````#7U]O;&1T<F%P```````#G
- M\`````1?36%T:%1R86YS0F%S90`````#]`````-?36%T:$)A<V4```````/XY
- M````!5]-871H265E941O=6)"87-"87-E```#_`````9?36%T:$EE965$;W5BQ
- M5')A;G-"87-E``````0``````E]?2#)?96YD```$!`````````/R```#ZP``"
- &``$```/RV
- ``
- end
- size 15576
- SHAR_EOF
- cat << \SHAR_EOF > MRDates.c
- /*
- MRDates - AmigaDOS date support routines.
- 07/03/88
-
- This package is a hybrid of code from Thad Floryan, Doug Merrit and
- myself. I wanted a reliable set of AmigaDOS date conversion routines
- and this combination seems to work pretty well. The star of the show
- here is Thad's algorithm for converting the "days elapsed" field of
- an AmigaDOS DateStamp, using an intermediate Julian date format. I
- lifted/embellished some of the data structures from Doug's ShowDate
- package and wrote the DateToDS function.
-
- History: (most recent change first)
-
- 12/31/88 -MRR-
- StrToDS was not handling the null string properly.
-
- 11/01/88 -MRR- Added Unix-style documentation.
-
- 07/03/88 - Changed some names:
- Str2DS => StrToDS
- DS2Str => DSToStr
- */
-
- #define MRDATES
- #include "MRDates.h"
- #include <exec/types.h>
- #include <ctype.h>
-
-
- char *index();
-
- #define DATE_SEPARATORS "/:-."
- #define MINS_PER_HOUR 60
- #define SECS_PER_MIN 60
- #define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR)
- #define TICS_PER_SEC 50
-
- #define YEARS_PER_CENTURY 100
-
-
- /*
- definitions to calculate current date
- */
- #define FEB 1 /* index of feb. in table (for leap years) */
- #define DAYS_PER_WEEK 7
- #define DAYS_PER_YEAR 365
- #define YEARS_PER_LEAP 4
- #define START_YEAR 1978
- #define FIRST_LEAP_YEAR 1980
- #define LEAP_ADJUST (FIRST_LEAP_YEAR - START_YEAR)
- #define LEAP_FEB_DAYS 29
- #define NORM_FEB_DAYS 28
- #define IsLeap(N) (((N) % YEARS_PER_LEAP) ? 0 : 1)
-
-
- /* FUNCTION
- DSToDate - convert a DateStamp to a DATE.
-
- SYNOPSIS
- int DSToDate(dateStamp, date)
- struct DateStamp *dateStamp;
- DATE *date;
-
- DESCRIPTION
- Extracts the date components from an AmigaDOS datestamp.
- The calculations herein use the following assertions:
-
- 146097 = number of days in 400 years per 400 * 365.2425 = 146097.00
- 36524 = number of days in 100 years per 100 * 365.2425 = 36524.25
- 1461 = number of days in 4 years per 4 * 365.2425 = 1460.97
-
- AUTHOR
- Thad Floryan, 12-NOV-85
- Mods by Mark Rinfret, 04-JUL-88
-
- SEE ALSO
- Include file MRDates.h.
-
- */
-
- #define DDELTA 722449 /* days from Jan.1,0000 to Jan.1,1978 */
-
- static int mthvec[] =
- {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
-
- int
- DSToDate(ds, date)
- long *ds; DATE *date;
-
- {
-
- long jdate, day0, day1, day2, day3;
- long year, month, day, temp;
-
- jdate = ds[0] + DDELTA; /* adjust internal date to Julian */
-
- year = (jdate / 146097) * 400;
- day0 = day1 = jdate %= 146097;
- year += (jdate / 36524) * 100;
- day2 = day1 %= 36524;
- year += (day2 / 1461) * 4;
- day3 = day1 %= 1461;
- year += day3 / 365;
- month = 1 + (day1 %= 365);
- day = month % 30;
- month /= 30;
-
- if ( ( day3 >= 59 && day0 < 59 ) ||
- ( day3 < 59 && (day2 >= 59 || day0 < 59) ) )
- ++day1;
-
- if (day1 > mthvec[1 + month]) ++month;
- day = day1 - mthvec[month];
- date->Dyear = year;
- date->Dmonth = month;
- date->Dday = day;
- date->Dweekday = ds[0] % DAYS_PER_WEEK;
-
- temp = ds[1]; /* get ds_Minute value */
- date->Dhour = temp / MINS_PER_HOUR;
- date->Dminute = temp % MINS_PER_HOUR;
- date->Dsecond = ds[2] / TICS_PER_SEC;
- return 0;
- }
-
- /* FUNCTION
- DateToDS(date, dateStamp)
-
- SYNOPSIS
- void DateToDS(date, dateStamp)
- DATE *date;
- struct DateStamp *dateStamp;
-
- DESCRIPTION
- DateToDS converts the special DATE format to a DateStamp.
- */
-
- DateToDS(date, ds)
- DATE *date; long *ds;
- {
- long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear;
- int month;
-
- /* Note the special handling for year < START_YEAR. In this case,
- * the other fields are not even checked - the user just gets the
- * "start of time".
- */
- if ((thisYear = date->Dyear) < START_YEAR) {
- ds[0] = ds[1] = ds[2] = 0;
- return;
- }
- if (IsLeap(thisYear))
- calendar[FEB].Mdays = LEAP_FEB_DAYS;
-
- thisDay = date->Dday - 1;
- thisMonth = date->Dmonth -1;
- yearsElapsed = thisYear - START_YEAR;
- leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP;
- daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears;
- for (month = 0; month < thisMonth; ++month)
- daysElapsed += calendar[month].Mdays;
- daysElapsed += thisDay;
- calendar[FEB].Mdays = NORM_FEB_DAYS;
- ds[0] = daysElapsed;
- ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute;
- ds[2] = date->Dsecond * TICS_PER_SEC;
- }
- /* FUNCTION
- CompareDS - compare two DateStamp values.
-
- SYNOPSIS
- int CompareDS(date1, date2)
- struct DateStamp *date1, *date2;
-
- DESCRIPTION
- CompareDS performs an ordered comparison between two DateStamp
- values, returning the following result codes:
-
- -1 => date1 < date2
- 0 => date1 == date2
- 1 => date1 > date2
-
- NOTE:
- This routine makes an assumption about the DateStamp structure,
- specifically that it can be viewed as an array of 3 long integers
- in days, minutes and ticks order.
- */
-
- int
- CompareDS(d1, d2)
- long *d1, *d2;
- {
- USHORT i;
- long compare;
-
- for (i = 0; i < 3; ++i) {
- if (compare = (d1[i] - d2[i])) {
- if (compare < 0) return -1;
- return 1;
- }
- }
- return 0; /* dates match */
- }
-
- /* FUNCTION
- DSToStr - convert a DateStamp to a formatted string.
-
- SYNOPSIS
- void DSToStr(str,fmt,d)
- char *str, *fmt;
- struct DateStamp *d;
-
- DESCRIPTION
- DSToStr works a little like sprintf. It converts a DateStamp
- to an ascii formatted string. The formatting style is dependent
- upon the contents of the format string, fmt, passed to this
- function.
-
- The content of the format string is very similar to that
- for printf, with the exception that the following letters
- have special significance:
- y => year minus 1900
- Y => full year value
- m => month value as integer
- M => month name
- d => day of month (1..31)
- D => day name ("Monday".."Sunday")
- h => hour in twenty-four hour notation
- H => hour in twelve hour notation
- i => 12 hour indicator for H notation (AM or PM)
- I => same as i
- n => minutes (sorry...conflict with m = months)
- N => same as n
- s => seconds
- S => same as s
-
- All other characters are passed through as part of the normal
- formatting process. The following are some examples with
- Saturday, July 18, 1987, 13:53 as an input date:
-
- "%y/%m/%d" => 87/7/18
- "%02m/%02d/%2y" => 07/18/87
- "%D, %M %d, %Y" => Saturday, July 18, 1987
- "%02H:%02m i" => 01:53 PM
- "Time now: %h%m" => Time now: 13:53
-
- */
- void
- DSToStr(str,fmt,d)
- char *str, *fmt; long *d;
- {
- DATE date;
- char fc,*fs,*out;
- USHORT ivalue;
- char new_fmt[256]; /* make it big to be "safe" */
- USHORT new_fmt_lng;
- char *svalue;
-
- DSToDate(d, &date); /* convert DateStamp to DATE format */
-
- *str = '\0'; /* insure output is empty */
- out = str;
- fs = fmt; /* make copy of format string pointer */
-
- while (fc = *fs++) { /* get format characters */
- if (fc == '%') { /* formatting meta-character? */
- new_fmt_lng = 0;
- new_fmt[new_fmt_lng++] = fc;
- /* copy width information */
- while (isdigit(fc = *fs++) || fc == '-')
- new_fmt[new_fmt_lng++] = fc;
-
- switch (fc) { /* what are we trying to do? */
- case 'y': /* year - 1980 */
- ivalue = date.Dyear % 100;
- write_int:
- new_fmt[new_fmt_lng++] = 'd';
- new_fmt[new_fmt_lng] = '\0';
- sprintf(out,new_fmt,ivalue);
- out = str + strlen(str);
- break;
- case 'Y': /* full year value */
- ivalue = date.Dyear;
- goto write_int;
-
- case 'm': /* month */
- ivalue = date.Dmonth;
- goto write_int;
-
- case 'M': /* month name */
- svalue = calendar[date.Dmonth - 1].Mname;
- write_str:
- new_fmt[new_fmt_lng++] = 's';
- new_fmt[new_fmt_lng] = '\0';
- sprintf(out,new_fmt,svalue);
- out = str + strlen(str);
- break;
-
- case 'd': /* day */
- ivalue = date.Dday;
- goto write_int;
-
- case 'D': /* day name */
- svalue = dayNames[d[0] % DAYS_PER_WEEK];
- goto write_str;
-
- case 'h': /* hour */
- ivalue = date.Dhour;
- goto write_int;
-
- case 'H': /* hour in 12 hour notation */
- ivalue = date.Dhour;
- if (ivalue >= 12) ivalue -= 12;
- goto write_int;
-
- case 'i': /* AM/PM indicator */
- case 'I':
- if (date.Dhour >= 12)
- svalue = "PM";
- else
- svalue = "AM";
- goto write_str;
-
- case 'n': /* minutes */
- case 'N':
- ivalue = date.Dminute;
- goto write_int;
-
- case 's': /* seconds */
- case 'S':
- ivalue = date.Dsecond;
- goto write_int;
-
- default:
- /* We are in deep caca - don't know what to do with this
- * format character. Copy the raw format string to the
- * output as debugging information.
- */
- new_fmt[new_fmt_lng++] = fc;
- new_fmt[new_fmt_lng] = '\0';
- strcat(out, new_fmt);
- out = out + strlen(out); /* advance string pointer */
- break;
- }
- }
- else
- *out++ = fc; /* copy literal character */
- }
- *out = '\0'; /* terminating null */
- }
-
- /* FUNCTION
- StrToDS - convert a string to a DateStamp.
-
- SYNOPSIS
- int StrToDS(string, date)
- char *string;
- struct DateStamp *date;
-
- DESCRIPTION
- StrToDS expects its string argument to contain a date in
- MM/DD/YY HH:MM:SS format. The time portion is optional.
- StrToDS will attempt to convert the string to a DateStamp
- representation. If successful, it will return 0. On
- failure, a 1 is returned.
-
- */
-
- int
- StrToDS(str, d)
- char *str; long *d;
- {
- register char c;
- int count;
- int i, item;
- DATE date; /* unpacked DateStamp */
- char *s;
-
- int values[3];
- int value;
-
- s = str;
- for (item = 0; item < 2; ++item) { /* item = date, then time */
- for (i = 0; i < 3; ++i) values[i] = 0;
- count = 0;
- while (c = *s++) { /* get date value */
- if (c <= ' ')
- break;
-
- if (isdigit(c)) {
- value = 0;
- do {
- value = value*10 + c - '0';
- c = *s++;
- } while (isdigit(c));
- if (count == 3) {
- bad_value:
- #ifdef DEBUG
- puts("Error in date-time format.\n");
- printf("at %s: values(%d) = %d, %d, %d\n",
- s, count, values[0], values[1], values[2]);
- #endif
- return 1;
- }
- values[count++] = value;
- if (c <= ' ')
- break;
- }
- else if (! index(DATE_SEPARATORS, c) )
- goto bad_value; /* Illegal character - quit. */
- } /* end while */
- if (item) { /* Getting time? */
- date.Dhour = values[0];
- date.Dminute = values[1];
- date.Dsecond = values[2];
- }
- else { /* Getting date? */
-
- /* It's OK to have a null date string, but it's not OK to specify only
- 1 or 2 of the date components.
- */
- if (count && count != 3)
- goto bad_value;
- date.Dmonth = values[0];
- date.Dday = values[1];
- date.Dyear = values[2];
- if (date.Dyear == 0) {
- date.Dyear = START_YEAR;
- date.Dday = 1;
- }
- else {
- if (date.Dyear < (START_YEAR - 1900) )
- date.Dyear += 100;
- date.Dyear += 1900;
- }
- }
- } /* end for */
- DateToDS(&date, d);
- return 0;
- } /* StrToDS */
-
-
- #ifdef DEBUG
- #include "stdio.h"
- main(ac, av)
- int ac;
- char **av;
- {
- long datestamp[3]; /* A little dangerous with Aztec */
- long datestamp2[3];
- DATE date, oldDate;
- long day, lastDay;
- int errors = 0;
-
- /*
- * display results from DateStamp() (hours:minutes:seconds)
- */
- DateStamp(datestamp);
-
- /*
- * display results from DSToDate() (e.g. "03-May-88")
- */
- DSToDate(datestamp, &date);
- printf("Current date: %02d-%s-%02d\n",
- date.Dday, calendar[ date.Dmonth - 1].Mname,
- (date.Dyear % YEARS_PER_CENTURY));
-
- printf("Current time: %02d:%02d:%02d\n",
- date.Dhour, date.Dminute, date.Dsecond);
-
- printf("\nDoing sanity check through year 2000...\n\t");
- lastDay = (2000L - START_YEAR) * 365L;
- lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP;
- for (day = 0; day <= lastDay; ++day) {
- if (day % 1000 == 0) {
- printf(" %ld", day);
- fflush(stdout);
- }
- datestamp[0] = day;
- datestamp[1] = MINS_PER_HOUR - 1;
- datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1);
- DSToDate(datestamp, &date);
- if (day && date == oldDate) {
- printf("Got same date for days %d, %d: %02d-%s-%02d\n",
- day - 1, day,
- date.Dday,
- calendar[ date.Dmonth - 1 ].Mname,
- (date.Dyear % YEARS_PER_CENTURY));
-
- if (++errors == 10)
- exit(1);
- }
- DateToDS(&date, datestamp2);
- if (day != datestamp2[0]) {
- printf("\nConversion mismatch at day %ld!\n", day);
- printf("\tBad value = %ld", datestamp2[0]);
- printf("\tDate: %02d-%s-%02d\n",
- date.Dday,
- calendar[ date.Dmonth -1 ].Mname,
- (date.Dyear % YEARS_PER_CENTURY));
- if (++errors == 10)
- exit(1);
- }
- oldDate = date;
- }
- printf("\nSanity check passed.\n");
- } /* main() */
- #endif
-
- SHAR_EOF
- cat << \SHAR_EOF > MRDates.h
- /* MRDates.h - Declarations for types and variables used by MRDates. */
-
- typedef struct {
- int Dyear; /* year AD (e.g. 1987) */
- int Dmonth; /* month of year (0-11) */
- int Dday; /* day in month (1-31) */
- int Dhour; /* 0-23 */
- int Dminute; /* 0-59 */
- int Dsecond; /* 0-59 */
- int Dweekday; /* day of week (Sun=0) */
- } DATE;
-
- typedef struct {
- char *Mname;
- int Mdays;
- } CalEntry;
-
- #ifdef MRDATES
- CalEntry calendar[12] = {
- { "Jan", 31 }, { "Feb", 28 }, { "Mar", 31 }, { "Apr", 30 },
- { "May", 31 }, { "Jun", 30 }, { "Jul", 31 }, { "Aug", 31 },
- { "Sep", 30 }, { "Oct", 31 }, { "Nov", 30 }, { "Dec", 31 }
- };
- #else
- extern CalEntry calendar[12];
- #endif
-
- #ifdef MRDATES
- char *dayNames[7] = {
- "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
- };
- #else
- extern char *dayNames[7];
- #endif
-
- SHAR_EOF
- cat << \SHAR_EOF > Makefile
- # Makefile for DiffDir program
-
- CFLAGS = -n
-
- OBJ = DiffDir.o MRDates.o
-
- DiffDir: $(OBJ)
- ln -w -g -o DiffDir $(OBJ) -lc
-
- clean:
- delete (#?.o|#?.dbg)
-
- SRC = DiffDir.c MRDates.h MRDates.c Makefile
- BIN = DiffDir.DOC DiffDir Sample.Output
-
- zoo: $(SRC) $(BIN)
- zoo a DiffDir $(SRC) $(BIN)
-
-
- SHAR_EOF
- cat << \SHAR_EOF > Sample.Output
- *** 'dir1/DifferentType' is a directory
- *** but 'dir2/DifferentType' is a file!
- *** Mismatch: comments
- dir1/
- 12-31-88 15:48:41 -----rwed 18 File2
- : This is file 2 in level 1 of dir 1.
- ------------------------------------
- dir2/
- 12-31-88 15:48:41 -----rwed 18 File2
- ====================================
- *** Mismatch: names
- dir1/
- 12-31-88 15:48:53 -----rwed 18 File3
- ------------------------------------
- dir2/
- 12-31-88 15:48:53 -----rwed 18 file3
- ====================================
- *** Mismatch: dates sizes
- dir1/Level2/
- 12-31-88 15:53:10 -----rwed 81 File1
- ------------------------------------
- dir2/Level2/
- 12-31-88 15:48:33 -----rwed 18 File1
- ====================================
- *** File missing: 'dir1/Level2/File2'
- *** File missing: 'dir2/Level2/Level3/File1'
- SHAR_EOF
- # End of shell archive
- exit 0
- --
- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page
- Have five nice days.
-